#include <fs.h>
#include <log.h>
#include <ds/lru.h>
#include <mm/slab.h>
#include <mm/valloc.h>

extern kmem_cache_t* dentry_kmem_cache;
extern lru_t* dentry_lru, * inode_lru;
extern hbkt_t* dentry_cache;

static hbkt_t* dcache_hash(dentry_t* parent, u32* hash) {
    u32 _hash = *hash;
    // 确保低位更加随机
    _hash = _hash ^ (_hash >> FS_HASHBITS);
    // 与parent的指针值做加法，来减小碰撞的可能性。
    _hash += (u32)parent;
    *hash = _hash;
    return &dentry_cache[_hash & FS_HASH_MASK];
}

dentry_t* fs_alloc_dentry(dentry_t* parent, hstr_t* name) {
    dentry_t* dentry = take_obj(dentry_kmem_cache);
    if (!dentry) {
        lru_evict_half(dentry_lru);

        if (!(dentry = take_obj(dentry_kmem_cache))) {
            return NULL;
        }
    }

    memset(dentry, 0, sizeof(*dentry));
    list_head_init(&dentry->children);
    list_head_init(&dentry->siblings);
    mutex_init(&dentry->lock);

    dentry->ref_count = ATOMIC_VAR_INIT(0);
    dentry->name = HHSTR(vzalloc(FS_NAME_MAX), 0, 0);

    hstr_copy(&dentry->name, name);

    if (parent) {
        dentry->sb = parent->sb;
        dentry->mnt = parent->mnt;
    }

    lru_use_one(dentry_lru, &dentry->lru);

    return dentry;
}

void fs_free_dentry(dentry_t* dentry) {
    assert(dentry->ref_count == 1);

    if (dentry->inode) {
        assert(dentry->inode->link_count > 0);
        dentry->inode->link_count--;
    }

    fs_dcache_remove(dentry);

    dentry_t* pos, * n;
    list_for_each(pos, n, &dentry->children, siblings) {
        fs_dcache_remove(pos);
    }

    vfree(dentry->name.val);
    vfree(dentry);
}

dentry_t* fs_dcache_lookup(dentry_t* parent, hstr_t* name) {
    if (!name->len || HSTR_EQ(name, &fs_path_dot))
        return parent;

    if (HSTR_EQ(name, &fs_path_dotdot))
        return parent->parent;

    u32 hash = name->hash;
    hbkt_t* bkt = dcache_hash(parent, &hash);
    if (bkt->head) {
        dentry_t* pos, * n;
        bucket_foreach(bkt, pos, n, hash_list) {
            if (hash == pos->name.hash) {
                return pos;
            }
        }
    }
    return NULL;
}

void fs_dcache_add(dentry_t* parent, dentry_t* dentry) {
    assert(parent);

    atomic_fetch_add(&dentry->ref_count, 1);
    dentry->parent = parent;
    list_append(&parent->children, &dentry->siblings);

    hbkt_t* bkt = dcache_hash(parent, &dentry->name.hash);
    hlist_add(&bkt->head, &dentry->hash_list);
}

void fs_dcache_remove(dentry_t* dentry) {
    assert(dentry);
    assert(dentry->ref_count == 1);

    list_remove(&dentry->siblings);
    hlist_remove(&dentry->hash_list);

    dentry->parent = NULL;
    atomic_fetch_sub(&dentry->ref_count, 1);
}

void fs_dcache_rehash(dentry_t* parent, dentry_t* dentry) {
    assert(parent);

    hstr_rehash(&dentry->name);
    fs_dcache_remove(dentry);
    fs_dcache_add(parent, dentry);
}